package com.wfs.datastore;

import java.util.Date;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.Query.FilterOperator;

public class PairSessionDatastore {
	
	private static final String DATASTORE_ENTITY_SET_NAME= "Pair Session Data Set";
	private static final String DATASTORE_ENTITY_KEY_NAME= "PAIR_SESSION_DATA";
	
	private static final String SESSION_KIND = "Session";
	
	//Kind properties
	private static final String TOKEN_PROPERTY= "token";
	private static final String STATE_PROPERTY = "state";
	
	
	private static final String DEVICE_RID_PROPERTY="device_rid";
	
	private static final String SESSION_START_TIME_PROPERTY= "session_start_time";
	
	//State constants
	public static final int ACKNOWLEDGED_STATE = 1;
	public static final int TERMINATED_STATE = 2;
	public static final int CONNECTED_STATE = 3;
	
	private static DatastoreService d = DatastoreServiceFactory.getDatastoreService();

	
	/**
	 * Pair Session Datastore constructor, initializes db schema
	 */
	public PairSessionDatastore(){
		initializeSchema();
	}
	
	/**
	 * Initializes entity set in datastore service if no such entity exists already,
	 * thus preserving idempotency (since this method is called upon construction of object)
	 */
	private static void initializeSchema(){
		Query q = new Query(DATASTORE_ENTITY_SET_NAME);
		q.setKeysOnly();
		if(d.prepare(q).asSingleEntity() == null) {
			Transaction txn = d.beginTransaction();    
			Entity e = new Entity(DATASTORE_ENTITY_SET_NAME, DATASTORE_ENTITY_KEY_NAME);
			d.put(e);
			txn.commit();
		}
	}
	
	/**
	 * Put session entry in the datastore using unique token, overwrites existing sessions if token matches 
	 * 
	 * @param token unique token
	 * @param state connection state 
	 * 				ACKNOWLEDGED_STATE = 1
	 * 				TERMINATED_STATE = 2
	 * 				CONNECTED_STATE = 3
	 * @param device_ip ip of the device to connect to
	 * @param device_port port of the device to connect to
	 * @param device_type device type specifications
	 * @return true if update successful, false if no existing token is found in the datastore
	 */
	public static void putSession(String token, int state, String device_rid){
		Key k = KeyFactory.createKey(DATASTORE_ENTITY_SET_NAME, DATASTORE_ENTITY_KEY_NAME); 
		Query q = new Query(SESSION_KIND);
		q.setAncestor(k);
		q.addFilter(TOKEN_PROPERTY, FilterOperator.EQUAL, token);
		Transaction txn = d.beginTransaction();     
		Entity e = d.prepare(q).asSingleEntity();		
		if(e == null){
			e = new Entity(SESSION_KIND,k);
			e.setProperty(TOKEN_PROPERTY, token);
			Date date = new Date();
			e.setProperty(SESSION_START_TIME_PROPERTY, date.getTime());
		}
			e.setProperty(STATE_PROPERTY, state);
			e.setProperty(DEVICE_RID_PROPERTY, device_rid);
			d.put(e);
			txn.commit();
	}
	
	/**
	 * Deletes all terminated sessions and non-connected state sessions that have existed for 10 minutes
	 */
	public static void purge(){
		Query q = new Query(SESSION_KIND);
		q.setKeysOnly();
		q.addFilter(STATE_PROPERTY, FilterOperator.LESS_THAN, CONNECTED_STATE);
		Date date = new Date();
		q.addFilter(SESSION_START_TIME_PROPERTY, FilterOperator.GREATER_THAN, date.getTime()+10*60*1000);
		Iterable<Entity> i = d.prepare(q).asIterable();
		for(Entity e : i)
			d.delete(e.getKey());
	}
	
	/**
	 * Queries for the state of the connection for given token
	 * @param token desired token to be queried
	 * @return int constant for connection state
	 * 				ACKNOWLEDGED_STATE = 1,
	 * 				TERMINATED_STATE = 2,
	 * 				CONNECTED_STATE = 3,
	 * 				if no such token was found returns -1
	 */
	public static int queryState(String token){
		Key k = KeyFactory.createKey(DATASTORE_ENTITY_SET_NAME, DATASTORE_ENTITY_KEY_NAME); 
		Query q = new Query(SESSION_KIND);
		q.setAncestor(k);
		q.addFilter(TOKEN_PROPERTY, FilterOperator.EQUAL, token);
		Entity e = d.prepare(q).asSingleEntity();
		if(e != null)
			return (Integer)e.getProperty(STATE_PROPERTY);
		return -1;	
	}
	
	/**
	 * Queries for RID attached to token
	 * @param token desired token to be queried
	 * @return RID string
	 * 				if no such token was found returns null
	 */
	public static String queryRID(String token){
		Key k = KeyFactory.createKey(DATASTORE_ENTITY_SET_NAME, DATASTORE_ENTITY_KEY_NAME); 
		Query q = new Query(SESSION_KIND);
		q.setAncestor(k);
		q.addFilter(TOKEN_PROPERTY, FilterOperator.EQUAL, token);
		Entity e = d.prepare(q).asSingleEntity();
		if(e != null)
			return (String)e.getProperty(DEVICE_RID_PROPERTY);
		return null;	
	}
	

	

}
